package com.sq.util.web;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.logging.log4j.core.util.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;

import com.sq.util.JsonUtil;
import com.sq.util.exception.CommonException;
import com.sq.util.page.PageQuery;
import com.sq.util.web.initer.FormIniter;
import com.sq.util.web.initer.JsonIniter;

/**
 * @author sq
 *
 */
public abstract class BaseArgumentResolver {

    private static final Logger log = LoggerFactory.getLogger(BaseArgumentResolver.class);

    private final static String JSON_TYPE = "application/json";

    private static final String JSON_MAP_DATA = "JSON_MAP_DATA";

    protected abstract boolean isMatch(String clsName);

    public boolean supportsParameter(MethodParameter methodParameter) {
        return isMatch(methodParameter).isMatch;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
            NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        Result result = isMatch(methodParameter);
        if (JSON_TYPE.equals(request.getHeader("Content-Type"))) {
            Object obj = getJsonBodyData(nativeWebRequest, result.isList);
            if (result.isList) {
                return JsonIniter.initList((List<Map>) obj, result.cls);
            }
            return JsonIniter.initInstance((Map) obj, result.cls);
        }
        if (result.isList) {
            return FormIniter.initList(request, result.cls);
        } else {
            return FormIniter.initInstance(request, result.cls);
        }
    }

    private Result isMatch(MethodParameter methodParameter) {
        String className = methodParameter.getParameterType().getName();
        if (className.equals("java.util.List")) {
            Type type = methodParameter.getGenericParameterType();
            if (!(type instanceof ParameterizedType)) {
                return new Result(false, false, null);
            }
            Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
            for (Type t : actualTypeArguments) {
                if (!isMatch(t.getTypeName())) {
                    continue;
                }
                try {
                    return new Result(true, true, Class.forName(t.getTypeName()));
                } catch (ClassNotFoundException e) {
                    log.error(e.getMessage(), e);
                    throw new CommonException(e.getMessage());
                }
            }
        } else if (isMatch(methodParameter.getParameterType().getName())) {
            return new Result(true, false, methodParameter.getParameterType());
        } else if (methodParameter.getParameterType().getName().equals(PageQuery.class.getName())) {
            return new Result(true, false, methodParameter.getParameterType());
        }
        return new Result(false, false, null);
    }

    private Object getJsonBodyData(NativeWebRequest webRequest, boolean isList) throws Exception {
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        Object obj = webRequest.getAttribute(JSON_MAP_DATA, NativeWebRequest.SCOPE_REQUEST);
        if (obj != null) {
            return obj;
        }
        // 没有就从请求中读取
        String s = IOUtils.toString(servletRequest.getReader());
        if (isList) {
            obj = JsonUtil.toListObject(s, Map.class);
        } else {
            obj = JsonUtil.toMap(s);
        }
        webRequest.setAttribute(JSON_MAP_DATA, obj, NativeWebRequest.SCOPE_REQUEST);
        return obj;
    }

    private static class Result {
        boolean isMatch;
        boolean isList;
        Class<?> cls;

        public Result(boolean isMatch, boolean isList, Class<?> cls) {
            super();
            this.isMatch = isMatch;
            this.isList = isList;
            this.cls = cls;
        }

    }
}
